import logging
import yaml
from urllib.parse import parse_qs, urlparse

import requests

from redash.query_runner import *
from redash.utils import json_dumps

logger = logging.getLogger(__name__)

COLUMN_TYPES = {
    'date': (
        'firstVisitDate', 'firstVisitStartOfYear', 'firstVisitStartOfQuarter',
        'firstVisitStartOfMonth', 'firstVisitStartOfWeek',
    ),
    'datetime': (
        'firstVisitStartOfHour', 'firstVisitStartOfDekaminute', 'firstVisitStartOfMinute',
        'firstVisitDateTime', 'firstVisitHour', 'firstVisitHourMinute'

    ),
    'int': (
        'pageViewsInterval', 'pageViews', 'firstVisitYear', 'firstVisitMonth',
        'firstVisitDayOfMonth', 'firstVisitDayOfWeek', 'firstVisitMinute',
        'firstVisitDekaminute',
    ),
}

for type_, elements in COLUMN_TYPES.items():
    for el in elements:
        if 'first' in el:
            el = el.replace('first', 'last')
            COLUMN_TYPES[type_] += (el, )


def parse_ym_response(response):
    columns = []
    dimensions_len = len(response['query']['dimensions'])

    for h in response['query']['dimensions'] + response['query']['metrics']:
        friendly_name = h.split(':')[-1]
        if friendly_name in COLUMN_TYPES['date']:
            data_type = TYPE_DATE
        elif friendly_name in COLUMN_TYPES['datetime']:
            data_type = TYPE_DATETIME
        else:
            data_type = TYPE_STRING
        columns.append({'name': h, 'friendly_name': friendly_name, 'type': data_type})

    rows = []
    for num, row in enumerate(response['data']):
        res = {}
        for i, d in enumerate(row['dimensions']):
            res[columns[i]['name']] = d['name']
        for i, d in enumerate(row['metrics']):
            res[columns[dimensions_len + i]['name']] = d
            if num == 0 and isinstance(d, float):
                columns[dimensions_len + i]['type'] = TYPE_FLOAT
        rows.append(res)

    return {'columns': columns, 'rows': rows}


class YandexMetrica(BaseSQLQueryRunner):
    should_annotate_query = False

    @classmethod
    def type(cls):
        # This is written with a "k" for backward-compatibility. See #2874.
        return "yandex_metrika"

    @classmethod
    def name(cls):
        return "Yandex Metrica"

    @classmethod
    def configuration_schema(cls):
        return {
            "type": "object",
            "properties": {
                "token": {
                    "type": "string",
                    "title": "OAuth Token"
                }
            },
            "required": ["token"],
        }

    def __init__(self, configuration):
        super(YandexMetrica, self).__init__(configuration)
        self.syntax = 'yaml'
        self.host = 'https://api-metrica.yandex.com'
        self.list_path = 'counters'

    def _get_tables(self, schema):

        counters = self._send_query('management/v1/{0}'.format(self.list_path))

        for row in counters[self.list_path]:
            owner = row.get('owner_login')
            counter = '{0} | {1}'.format(
                row.get('name', 'Unknown').encode('utf-8'), row.get('id', 'Unknown')
            )
            if owner not in schema:
                schema[owner] = {'name': owner, 'columns': []}

            schema[owner]['columns'].append(counter)

        return list(schema.values())

    def test_connection(self):
        self._send_query('management/v1/{0}'.format(self.list_path))

    def _send_query(self, path='stat/v1/data', **kwargs):
        token = kwargs.pop('oauth_token', self.configuration['token'])
        r = requests.get(
            '{0}/{1}'.format(self.host, path),
            headers={'Authorization': 'OAuth {}'.format(token)},
            params=kwargs
        )
        if r.status_code != 200:
            raise Exception(r.text)
        return r.json()

    def run_query(self, query, user):
        logger.debug("Metrica is about to execute query: %s", query)
        data = None
        query = query.strip()
        if query == "":
            error = "Query is empty"
            return data, error
        try:
            params = yaml.safe_load(query)
        except ValueError as e:
            logging.exception(e)
            error = str(e)
            return data, error

        if isinstance(params, dict):
            if 'url' in params:
                params = parse_qs(urlparse(params['url']).query, keep_blank_values=True)
        else:
            error = 'The query format must be JSON or YAML'
            return data, error

        try:
            data = json_dumps(parse_ym_response(self._send_query(**params)))
            error = None
        except Exception as e:
            logging.exception(e)
            error = str(e)
        return data, error


class YandexAppMetrica(YandexMetrica):
    @classmethod
    def type(cls):
        # This is written with a "k" for backward-compatibility. See #2874.
        return "yandex_appmetrika"

    @classmethod
    def name(cls):
        return "Yandex AppMetrica"

    def __init__(self, configuration):
        super(YandexAppMetrica, self).__init__(configuration)
        self.host = 'https://api.appmetrica.yandex.com'
        self.list_path = 'applications'


register(YandexMetrica)
register(YandexAppMetrica)
